#include "HBSD_Routing.h"
#include <exception>
#include <stdlib.h>
#include "Bundle.h"
#include "Bundles.h"
#include "XMLTree.h"
#include "HBSD.h"
#include <exception>
#include "Link.h"
#include "ConfigFile.h"
#include "HBSD_Policy.h"
#include "GBOF.h"
#include "Nodes.h"
#include "Policy.h"
#include "PeerListener.h"
#include "Requester.h"

using namespace std;


string HBSD_Routing::BPA_ATTR_EID;
string HBSD_Routing::defaultPolicy;


HBSD_Routing::HBSD_Routing()
{
	BPA_ATTR_EID.assign("eid");
	defaultPolicy.assign("HBSD_Policy");

	// Load and initialize the router's policy manager.
	string routerPolicyClassName =
		HBSD::routerConf->getstring("routerPolicyClass", defaultPolicy);
	try 
	{
		policyMgr =new HBSD_Policy(); 

		if (!policyMgr->init(this)) 	
		{
			HBSD::log->fatal(string("Unable to initialize Policy Manager: ") +	routerPolicyClassName);
			exit(1);
		} 
	} 
	catch (exception& e) 
	{
		if (HBSD::log->enabled(Logging::ERROR)) 
		{
			HBSD::log->error("Unable to load the Policy Manager: " + string(e.what()));
		}
		exit(1);
	}
}

HBSD_Routing::~HBSD_Routing()
{
}

void HBSD_Routing::initialized() 
{
	peerListener->init();
	// What we would like to do is get bundle, link and route reports so that we can populate our tables if HBSD were to be started after dtnd. Unfortunately, link reports don't provide the link_id so their usefulness is questionable. Bundle reports are only partially useful, since the local_id cannot be ascertained unless the bundle was locally initiated. Regardless, we make our requests.
	HBSD::requester->queryLink();
	HBSD::requester->queryBundle();
	HBSD::requester->queryRoute();
}

void HBSD_Routing::handler_bpa_root(XMLTree *bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	if (bpa->haveAttr(string("alert"))) 
	{
		if(string("shuttingDown").compare(bpa->getAttr(string("alert"))) == 0) 
		{
			bool verbose = HBSD::log->enabled(Logging::INFO);
			if (HBSD::routerConf->getBoolean(string("terminateWithDTN"),true)) 
			{
				if (verbose) 
				{
					HBSD::log->info(string("DTN daemon is terminating; HBSD will now terminate"));
				}
				exit(0);
			}
			if (verbose) 
			{
				HBSD::log->info(string("DTN daemon is terminating; HBSD will continue to execute"));
			}
		}
	}
}

void HBSD_Routing::handler_bundle_received_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	// Create the new bundle.
	Bundle* bundle = bundles->newBundle(event);
	if (bundle == NULL) 
	{
		// We can get here if it's a special bundle for this router.
		return;
	}
	// Do we know about the bundle's destination node? if not, go
	// ahead and create a Node object for it. We specifically do
	// this before informing the policy manager of the bundle.
	addDestNode(bundle);
	policyMgr->bundleReceived(bundle);
}


void HBSD_Routing::handler_data_transmitted_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty())
	{
		firstMessage(bpa);
	}
	try 
	{
		string linkId = event->getAttrRequired(string("link_id"));
		Link* link = links->getById(linkId);
		if (link == NULL) 
		{
			if (HBSD::log->enabled(Logging::ERROR)) 
			{

				HBSD::log->error("Unable to locate link id to complete transmission: " + linkId);
			}
			return;
		}
		link->dataTransmitted(atol(event->getAttrRequired(string("local_id")).c_str()),atoi(event->getAttrRequired(string("bytes_sent")).c_str()),atoi(event->getAttrRequired(string("reliably_sent")).c_str()));
	} 
	catch (exception & e) 
	{
			
	}
}


void HBSD_Routing::handler_bundle_report_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	try
	{
		// For each bundle in the report, add the bundle.
		int num = event->numChildElements();
		for (int n=0; n<num; n++) 
		{
			XMLTree* evt = event->getChildElement(n);
			Bundle* bundle = bundles->newBundleReported(evt);
			if (bundle == NULL) 
			{
				continue;
			}
			addDestNode(bundle);
			policyMgr->bundleReceived(bundle);
		}
	} 
	catch (exception& e) 
	{
		if (HBSD::log->enabled(Logging::ERROR)) 
		{
			HBSD::log->error(string("Ill-formed bundle_report received"));
		}
	}
}


void HBSD_Routing::handler_bundle_delivered_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	Bundle* bundle = bundles->eventDelivered(event);

	if (bundle != NULL) 
	{
		policyMgr->bundleDelivered(bundle);
	}
}


void HBSD_Routing::handler_bundle_delivery_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	
	Bundle* bundle = bundles->newBundleDelivery(event);
	if (bundle == NULL) 
	{
		return;
	}
	// If we inject a meta data bundle we'll see it just as if had been
	// sent to us from another EID. If we are the source, ignore it.
	if (localSource(bundle)) 
	{
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			HBSD::log->debug(string("Ignoring HBSD routing originated by this node"));
		}
		return;
	}
	peerListener->eventDelivery(event, bundle);
}


void HBSD_Routing::handler_bundle_expired_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	
	Bundle* bundle = bundles->eventExpired(event);

	if (bundle != NULL) 
	{
		policyMgr->bundleExpired(bundle);
	}
}


void HBSD_Routing::handler_bundle_injected_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	
	try 
	{
		Bundle* bundle = bundles->newInjectedBundle(event);
		if (bundle != NULL) 
		{
			XMLTree* el = event->getChildElementRequired(string("request_id"));
			policyMgr->bundleInjected(bundle->localId, el->getValue(), bundle);
		}
	} 
	catch (exception& e) 
	{
		HBSD::log->error(string("Error parsing injected bundle element"));
	}
}

void HBSD_Routing::handler_link_opened_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	Link* link = linkStructure(event);
	if (link != NULL) 
	{
		link->opened(event);
	} else 
	{
		HBSD::log->error(string("Opened unknown link"));
	}
}
	
void HBSD_Routing::handler_link_closed_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	
	Link* link = linkStructure(event);

	if (link != NULL) 
	{
		link->closed(event);
	} else 
	{
		HBSD::log->error(string("Closed unknown link"));
	}
}


void HBSD_Routing::handler_link_created_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	links->create(event);
}

void HBSD_Routing::handler_link_deleted_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	links->deleteEvt(event);
}

void HBSD_Routing::handler_route_report_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	routes->reportReceived(event);
}


void HBSD_Routing::firstMessage(XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		return;
	}
	if (bpa->haveAttr(BPA_ATTR_EID)) 
	{
		HBSD::setLocalEID(bpa->getAttr(BPA_ATTR_EID));
	} else 
	{
		return;
	}
}


void HBSD_Routing::addDestNode(Bundle* bundle) 
{
	if (!bundle->destEID.empty()) 
	{
		if (bundle->destEID.compare("") != 0) 
		{
			nodes->conditionalAdd(bundle->destEID);
		}
	}
}


bool HBSD_Routing::localSource(Bundle* bundle) 
{
	try 
	{
		string me = HBSD::localEID + string("/");
		// Ignore endpoint.
		string sourceEID = bundle->sourceURI.substr(0, me.length());
		if (sourceEID.compare(me) == 0) 
		{
			return true;
		}
	
		return false;
	} 
	catch (exception& e) 
	{
		return false;
	}
}


string HBSD_Routing::linkId(XMLTree* el) 
{
	if (el->haveAttr(string("link_id"))) 
	{
		return el->getAttr(string("link_id"));
	}
	return NULL;
}


Link* HBSD_Routing::linkStructure(XMLTree* el) 
{
	string id = linkId(el);
	if (id.empty()) 
	{
		return NULL;
	}
	return links->getById(id);
}


Link* HBSD_Routing::probableBusy(XMLTree* event) 
{
	try 
	{
		XMLTree* el = event->getChildElementRequired(string("link"));
		string uri = el->getChildElementRequired(string("remote_eid"))->getAttrRequired(string("uri"));
		return routes->getTemporalLink(GBOF::eidFromURI(uri), NULL, false);
	} 
	catch (exception& e) 
	{
		return NULL;
	}
}


void HBSD_Routing::handler_link_available_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	Link* link = linkStructure(event);
	if (link != NULL) 
	{
		link->available(event);
	} else 
	{
		if (HBSD::log->enabled(Logging::ERROR)) 
		{
			HBSD::log->error(string("Unknown link became available"));
		}
	}
}

void HBSD_Routing::handler_link_unavailable_event(XMLTree* event, XMLTree* bpa)
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	Link* link = linkStructure(event);
	if (link != NULL) 
	{
		link->unavailable(event);
	} else 
	{
		if (HBSD::log->enabled(Logging::ERROR)) 
		{
			HBSD::log->error(string("Unknown link became unavailable"));
		}
	}
}

void HBSD_Routing::handler_link_attribute_changed_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	Link* link = linkStructure(event);
	if (link != NULL) 
	{
		link->linkAttrsChanged(event);
	}
}


void HBSD_Routing::handler_contact_attribute_changed_event(XMLTree*  event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}

	Link* link = linkStructure(event);
	if (link != NULL) 
	{
		link->contactAttrsChanged(event);
	}
}

void HBSD_Routing::handler_link_busy_event(XMLTree* event, XMLTree*  bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	
	Link* link = probableBusy(event);
	if (link != NULL) 
	{
		link->busy(event);
	} else 
	{
		if (HBSD::log->enabled(Logging::WARN)) 
		{
			HBSD::log->warn(string("Unknown link became busy"));
		}
	}
}



void HBSD_Routing::handler_link_report_event(XMLTree* event, XMLTree* bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	// Link reports are of no value because they do not include
	// the link_id. Therefore it's very much recommended that
	// this router be started before dtnd. We will take the
	// opportunity to tell the user that, since we should only
	// get a report if we're started after dtnd.
	if (HBSD::log->enabled(Logging::WARN)) 
	{
		HBSD::log->warn(string("HBSD router should be started before the DTN daemon"));
	}
}

void HBSD_Routing::handler_route_add_event(XMLTree* event, XMLTree*  bpa) 
{
	if (HBSD::localEID.empty()) 
	{
		firstMessage(bpa);
	}
	routes->addRoute(event);
}

void HBSD_Routing::handler_route_delete_event(XMLTree* event, XMLTree* bpa)
{
}

void HBSD_Routing::handler_bundle_attributes_report_event(XMLTree *event, XMLTree *bpa) 
{
}

void HBSD_Routing::handler_eid_reachable_event(XMLTree *event, XMLTree *bpa)
{
}


void HBSD_Routing::handler_contact_report_event(XMLTree *event, XMLTree *bpa) 
{
}

void HBSD_Routing::handler_link_attributes_report_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_registration_expired_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_registration_removed_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_registration_added_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_intentional_name_resolved_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_custody_timeout_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_custody_signal_event(XMLTree *event, XMLTree *bpa)
{
}

void HBSD_Routing::handler_bundle_send_cancelled_event(XMLTree *event, XMLTree *bpa)
{
}



